#pragma once

class Vector3
{
public:
	static const Vector3 Zero;
	static const Vector3 One;
	static const Vector3 UnitX;
	static const Vector3 UnitY;
	static const Vector3 UnitZ;
	float X, Y, Z;

	Vector3() {}
	Vector3(float s) : X(s), Y(s), Z(s) {}
	Vector3(float x, float y, float z) : X(x), Y(y), Z(z) {}
	Vector3(const Vector3& v) : X(v.X), Y(v.Y), Z(v.Z) {}
	
	Vector3 operator^( const Vector3& v ) const { return Vector3(Y * v.Z - Z * v.Y, Z * v.X - X * v.Z, X * v.Y - Y * v.X); }
	float operator|( const Vector3& v ) const { return X*v.X + Y*v.Y + Z*v.Z; }
	Vector3 operator+( const Vector3& v ) const { return Vector3( X + v.X, Y + v.Y, Z + v.Z ); }
	Vector3 operator-( const Vector3& v ) const { return Vector3( X - v.X, Y - v.Y, Z - v.Z ); }
	Vector3 operator*( float Scale ) const { return Vector3( X * Scale, Y * Scale, Z * Scale ); }
	Vector3 operator/( float Scale ) const { const float RScale = 1.f/Scale; return Vector3( X * RScale, Y * RScale, Z * RScale ); }
	Vector3 operator*( const Vector3& v ) const { return Vector3( X * v.X, Y * v.Y, Z * v.Z ); }
	Vector3 operator/( const Vector3& v ) const { return Vector3( X / v.X, Y / v.Y, Z / v.Z ); }

	// Binary comparison operators.
	bool operator==( const Vector3& v ) const { return X==v.X && Y==v.Y && Z==v.Z; }
	bool operator!=( const Vector3& v ) const { return X!=v.X || Y!=v.Y || Z!=v.Z; }

	// Unary operators.
	Vector3 operator-() const { return Vector3( -X, -Y, -Z ); }

	// Assignment operators.
	Vector3 operator+=( const Vector3& v ) { X += v.X; Y += v.Y; Z += v.Z; return *this; }
	Vector3 operator-=( const Vector3& v ) { X -= v.X; Y -= v.Y; Z -= v.Z; return *this; }
	Vector3 operator*=( float Scale ) { X *= Scale; Y *= Scale; Z *= Scale; return *this; }
	Vector3 operator/=( float v ) { const float RV = 1.f/v; X *= RV; Y *= RV; Z *= RV; return *this; }
	Vector3 operator*=( const Vector3& v ) { X *= v.X; Y *= v.Y; Z *= v.Z; return *this; }
	Vector3 operator/=( const Vector3& v ) { X /= v.X; Y /= v.Y; Z /= v.Z; return *this; }
	
	float& operator[](int i) { return ((float*)this)[i]; }
	const float operator[](int i) const { return ((float*)this)[i]; }

	float SquareLength() const
	{
		return X * X + Y * Y + Z * Z;
	}

	float Length() const
	{
		return sqrt(X * X + Y * Y + Z * Z);
	}

	inline bool Normalize(float Tolerance = SMALL_NUMBER)
	{
		const float SquareSum = SquareLength();
		if (SquareSum > Tolerance)
		{
			const float Scale = appInvSqrt(SquareSum);
			X *= Scale; Y *= Scale; Z *= Scale;
			return true;
		}
		return false;
	}

	float NormalizeEx(float Tolerance = SMALL_NUMBER)
	{
		const float fLength = Length();
		if (fLength > Tolerance)
		{
			float fInvLength = 1.0f / fLength;
			X *= fInvLength;
			Y *= fInvLength;
			Z *= fInvLength;
		}
		return fLength;
	}

	int IsNearlyZero(float Tolerance=KINDA_SMALL_NUMBER) const
	{
		return Abs(X)<Tolerance &&	Abs(Y)<Tolerance &&	Abs(Z)<Tolerance;
	}

	bool IsZero() const
	{
		return X==0.f && Y==0.f && Z==0.f;
	}

	inline Vector3 UnsafeNormal() const
	{
		const float Scale = appInvSqrt(X*X + Y*Y + Z*Z);
		return Vector3(X * Scale, Y * Scale, Z * Scale );
	}

	inline void MakeFloor(const Vector3& v)
	{
		if (v.X < X) X = v.X;
		if (v.Y < Y) Y = v.Y;
		if (v.Z < Z) Z = v.Z;
	}
	
	inline void MakeCeil(const Vector3& v)
	{
		if (v.X > X) X = v.X;
		if (v.Y > Y) Y = v.Y;
		if (v.Z > Z) Z = v.Z;
	}
};
